/*
pm2105_i2c.cpp -- Arduino library to control Cubic PM2105 I2C

Copyright (c) 2025 zabbey.
All rights reserved.

This file is part of the library PM2105 I2C.

PM2105 I2C is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PM2105 I2C is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "pm2105_i2c.h"

/**
 * Start PM2105 I2C library
 */
void PM2105_I2C::begin(TwoWire *theWire)
{
	_theWire = theWire;
	//_theWire.begin();
	
	init();
}

void PM2105_I2C::init() {
    uint16_t data;
    _buffer[0] = PM2105_FRAME_HEADER;
    _buffer[1] = 0x7; // frame length
    _buffer[2] = PM2105_CONTROL_MODE;

    switch (PM2105_CONTROL_MODE) {
      case PM2105_CTRL_SET_UP_CONTINUOUSLY_MEASUREMENT:
        data = 0xFFFF;
        break;
      case PM2105_CTRL_SET_UP_CALIBRATION_COEFFICIENT:
        data = PM2105_CALIBRATION_COEFFICIENT;
        break;
      default:
        data = PM2105_MEASURING_TIME;
        break;
    }

    _buffer[3] = data >> 8;
    _buffer[4] = data & 0xFF;
    _buffer[5] = 0; // Reserved

    // Calculate checksum
    _buffer[6] = this->_buffer[0];

    for (uint8_t i = 1; i < 6; i++) {
      _buffer[6] ^= this->_buffer[i];
    }

    _theWire->beginTransmission(PM2105_ADDRESS);
    _theWire->write(this->_buffer, 7);
    _theWire->endTransmission();
  }

/**
 * Read result of measuring
 * @return {@code 0} Reading PM2008 value succeeded
 *         {@code 1} Buffer(index) is short
 *         {@code 2} Frame header is different
 *         {@code 4} Checksum is wrong
 */
uint8_t PM2105_I2C::measure()
{
    _theWire->requestFrom(PM2105_ADDRESS, 22);
    uint8_t idx = 0;

    while (_theWire->available()) { // slave may send less than requested
      uint8_t b = _theWire->read();
      _buffer[idx++] = b;
      if (idx == 22) break;
    }
	
    if (idx < 22) {
      return 1;
    }

    // Check frame header
    if (_buffer[0] != PM2105_FRAME_HEADER) {
      return 2;
    }

    // Check frame length
    if (_buffer[1] != 22) { 
      return 3;
    }

    // Check checksum
    uint8_t check_code = _buffer[0];
    for (uint8_t i = 1; i < 21; i++) {
      check_code ^= _buffer[i];
    }

    if (_buffer[21] != check_code) {
      return 4;
    }
    // Status
    status = _buffer[2];
    measuring_mode = (_buffer[9] << 8) + _buffer[10];
    calibration_coefficient = (_buffer[11] << 8) + _buffer[12];
    pm1p0_grimm = (_buffer[3] << 8) + _buffer[4];
    pm2p5_grimm = (_buffer[5] << 8) + _buffer[6];
    pm10_grimm = (_buffer[7] << 8) + _buffer[8];

    return 0;
}